<?php
/**
 * Copyright 2011 MaGdev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *    * The names of the authors may not be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @package      Mashape API-Documentor
 * @author       Marco Graetsch <info@magdev.de>
 * @copyright    2011 Marco Graetsch.development
 * @license      http://opensource.org/licenses/bsd-license.php New BSD License
 * @link         https://code.google.com/p/apidocumentor/
 */

error_reporting(E_ALL);

require_once 'mashdoc/Renderer/Interface.php';


/**
 * Mashape API-Documentor main class
 *
 * @author magdev
 */
class Mashdoc implements SplSubject
{
	const E_NO_STYLESHEET 		= -10;
	const E_NO_XML_DATA			= -20;
	const E_NO_CONTENT			= -30;
	const E_INVALID_ARGUMENT	= -40;
	const E_FILE_NOT_FOUND		= -50;
	const E_INVALID_TAG			= -60;
	const E_UNKNOWN_METHOD		= -70;
	const E_SYSTEM_MISCONFIG    = -100;


	/** @var Mashdoc_Renderer_Interface */
	private $_renderer = null;

	/** @var SplObjectStorage */
	private $_observer = null;

	/** @var DOMDocument */
	private $_apixml = null;

	/** @var array */
	private $_options = array(
		'theme' => 'default',
		'jquery' => '1.6.4',
		'jquerymobile' => '1.0rc1',
		'jqueryBaseURL' => 'http://code.jquery.com/',
		'useDataURI' => true,
		'root' => null,
		'doctype' => 'html',
		'mimetype' => 'text/html',
		'encoding' => 'UTF-8',
		'baseURL' => null,
	    'xslFile' => 'assets/api.xsl',
	    'cssFile' => 'assets/api.css',
	);



	/**
	 * Constructor
	 *
	 * @param array|null $options
	 * @param Mashdoc_Renderer_Interface|null $renderer
	 * @return void
	 */
	public function __construct($options = null, Mashdoc_Renderer_Interface $renderer = null)
	{
		$this->_observer = new SplObjectStorage();
		$this->setOptions($options);
		if (!is_null($renderer)) {
			$this->setRenderer($renderer);
		}
	}


	/**
	 * Load an api.xml file
	 *
	 * @param string $url
	 * @return Mashdoc
	 */
	public function loadApi($url)
	{
		$this->_apixml = new DOMDocument('1.0', $this->getOption('encoding'));
		$this->_apixml->load($url);
		$this->notify();
		return $this;
	}


	/**
	 * Set an renderer
	 *
	 * @param Mashdoc_Renderer_Interface
	 * @return Mashdoc
	 */
	public function setRenderer(Mashdoc_Renderer_Interface $renderer)
	{
		$this->_renderer = $renderer;
		if ($this->_renderer instanceof SplObserver) {
			$this->attach($this->_renderer);
			$this->notify();
		}
		return $this;
	}


	/**
	 * Get a renderer
	 *
	 * @param string $type Last part of the renderer classname
	 * @return Mashdoc_Renderer_Interface
	 * @throws InvalidArgumentException
	 */
	public function getRenderer($type = 'Xsl')
	{
		if (is_null($this->_renderer)) {
			$clsname = 'Mashdoc_Renderer_'.$type;
			if (class_exists($clsname, true)) {
				$renderer = new $clsname();
				$this->setRenderer($renderer);
			} else {
				throw new InvalidArgumentException('Invalid renderer: '.$clsname, self::E_INVALID_ARGUMENT);
			}
		}
		return $this->_renderer;
	}


	/**
	 * Get a metadata reader
	 *
	 * @param string $type
	 * @return Mashdoc_Metadata_Abstract
	 * @throws InvalidArgumentException
	 */
	public function getMetadata($type = 'Default')
	{
		$clsname = 'Mashdoc_Metadata_'.$type;
		if (class_exists($clsname, true)) {
			$metadata = new $clsname();
			$this->attach($metadata);
			return $metadata;
		}
		throw new InvalidArgumentException('Unknown metadata-handler: '.$clsname, self::E_INVALID_ARGUMENT);
	}


	/**
	 * Get the DOM object for the api.xml
	 *
	 * @return DOMDocument
	 */
	public function getApiDocument()
	{
		return $this->_apixml;
	}


	/**
	 * Set options for the documentor
	 *
	 * @param array $options
	 * @return Mashdoc
	 */
	public function setOptions($options)
	{
		if (is_array($options)) {
			foreach ($options as $k => $v) {
				$this->setOption($k, $v);
			}
		}
		return $this;
	}


	/**
	 * Get all options
	 *
	 * @return array
	 */
	public function getOptions()
	{
		return $this->_options;
	}


	/**
	 * Set a single option
	 *
	 * @param string $key
	 * @param mixed $value
	 * @return Mashdoc
	 */
	public function setOption($key, $value)
	{
		if (isset($this->_options[$key])) {
			$this->_options[$key] = $value;
			$this->notify();
		}
		return $this;
	}


	/**
	 * Get the value of a single option
	 *
	 * @param string $key
	 * @param mixed $default
	 * @return mixed
	 */
	public function getOption($key, $default = null)
	{
		if (isset($this->_options[$key])) {
			return $this->_options[$key];
		}
		return $default;
	}


	/**
	 * Output the generated code
	 *
	 * @param boolean $sendHeader
	 * @return Mashdoc
	 */
	public function output($sendHeader = true)
	{
		$renderer = $this->getRenderer();
		$renderer->output($sendHeader);
	    return $this;
	}


	/**
	 * Save the output to filesystem
	 *
	 * @param string $filename
	 * @return Mashdoc
	 */
	public function save($filename = 'index.html')
	{
	    $renderer = $this->getRenderer();
	    $ret = (boolean) file_put_contents($filename, (string) $renderer);
	    return $this;
	}


	/**
	 * Attach an observer
	 *
	 * @param SplObserver $observer
	 * @see SplObserver::attach()
	 * @return Mashdoc
	 */
	public function attach(SplObserver $observer)
	{
		if (!$this->_observer->contains($observer)) {
			$this->_observer->attach($observer);
			$observer->update($this);
		}
		return $this;
	}


	/**
	 * Detach an observer
	 *
	 * @param SplObserver $observer
	 * @see SplObserver::detach()
	 * @return Mashdoc
	 */
	public function detach(SplObserver $observer)
	{
		if ($this->_observer->contains($observer)) {
			$this->_observer->detach($observer);
		}
		return $this;
	}


	/**
	 * Notify observers
	 *
	 * @see SplSubject::notify()
	 * @return void
	 */
	public function notify()
	{
		foreach ($this->_observer as $o) {
			$o->update($this);
		}
	}


	/**
	 * Autoloading
	 *
	 * @param string $class
	 * @static
	 * @return boolean
	 */
	public static function autoload($class)
	{
		if (preg_match('/^Mashdoc.*/', $class)) {
			include_once lcfirst(str_replace('_', '/', $class)).'.php';
			return true;
		}
		return false;
	}


	/**
	 * Toggle debug mode
	 *
	 * @param boolean $enable
	 */
	public static function setDebug($enable = true)
	{
	    if ($enable) {
	        error_reporting(E_ALL);
            ini_set('display_errors', 1);
	    } else {
            ini_set('display_errors', 0);
	    }
	}
}

/** register autoloader */
spl_autoload_register(array('Mashdoc', 'autoload'));

/** Disable debug */
Mashdoc::setDebug(false);
?>